package cn.s1995.sloth.bizs.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.s1995.sloth.bizs.constant.BizCst;
import cn.s1995.sloth.bizs.constant.BizCst.CommonAuditState;
import cn.s1995.sloth.bizs.constant.CacheNames;
import cn.s1995.sloth.bizs.constant.SqlCst;
import cn.s1995.sloth.bizs.constant.TimeCst;
import cn.s1995.sloth.bizs.entity.*;
import cn.s1995.sloth.bizs.mapper.ShopMapper;
import cn.s1995.sloth.bizs.mapper.VideoCommentMapper;
import cn.s1995.sloth.bizs.mapper.VideoMapper;
import cn.s1995.sloth.bizs.oo.vo.VideoVO;
import cn.s1995.sloth.bizs.service.*;
import cn.s1995.sloth.common.core.constant.enums.hint.Hint;
import cn.s1995.sloth.common.core.util.R;
import cn.s1995.sloth.common.core.util.SpringContextHolder;
import cn.s1995.sloth.common.security.util.SecurityUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 视频
 *
 * @author Sasshi
 * @date 2023-12-12 10:51:22
 */
@Service
public class VideoServiceImpl extends ServiceImpl<VideoMapper, Video> implements VideoService {

    @Resource
    private VideoProdRecommendService videoProdRecommendService;
    @Resource
    private UserShareService userShareService;
    @Resource
    private UserLikeService userLikeService;
    @Resource
    private UserCollectionService userCollectionService;
    @Resource
    private UserFollowService userFollowService;
    @Resource
    private ShopMapper shopMapper;
    @Resource
    private VideoCommentMapper videoCommentMapper;

    @Override
    @Transactional
    public boolean saveVideoAndRecommend(Video video) {
    	VideoService videoService = SpringContextHolder.getBean(VideoService.class);
        if (ObjectUtil.isNotNull(video.getId())) {
            videoProdRecommendService.removeByVideoId(video.getId());
            updateById(video);
            videoService.syncCacheToDB(video.getId());
            videoService.removeCache(video.getId());
        } else {
            video.setDelFlag(false);
            if (ObjectUtil.isNull(video.getState())) {
                video.setState(CommonAuditState.approve);
            }
            save(video);
        }
        // 视频关联的推荐产品
        if (ObjectUtil.isNotEmpty(video.getVideoProdRecommendList())) {
            for (VideoProdRecommend videoProdRecommend : video.getVideoProdRecommendList()) {
                videoProdRecommend.setVideoId(video.getId());
            }
            videoProdRecommendService.saveBatch(new ArrayList<VideoProdRecommend>(video.getVideoProdRecommendList()));
        }
        return true;
    }

    @Override
    @Cacheable(value = CacheNames.video, key = "#id")
    public Video getVideoAndRecommend(Long id) {
        Video video = getById(id);
        if (ObjectUtil.isNotNull(video)) {
            video.setVideoProdRecommendList(videoProdRecommendService.getByVideoId(id));
        }
        return video;
    }

    @Override
    public List<Video> getRandomList(Integer count) {
        long dataCount = this.count(
                Wrappers.lambdaQuery(Video.class)
                        .eq(Video::getIsSale, BizCst.CommonBool.yes)
//					.eq(Video::getState, BizCst.CommonAuditState.approve)
        );
        if (dataCount == 0)
            return Collections.emptyList();
        List<Video> list = null;
        if (dataCount <= count) {
            list = this.list(
                    Wrappers.lambdaQuery(Video.class)
                            .eq(Video::getIsSale, BizCst.CommonBool.yes)
//						.eq(Video::getState, BizCst.CommonAuditState.approve)
            );
        } else {
            list = this.list(
                    Wrappers.lambdaQuery(Video.class)
                            .eq(Video::getIsSale, BizCst.CommonBool.yes)
//						.eq(Video::getState, BizCst.CommonAuditState.approve)
                            .last(SqlCst.getLimitOffset((long) (dataCount * Math.random()), Long.valueOf(count)))
            );
        }
        Collections.shuffle(list);
        return list;
    }

    @Override
    @CachePut(value = CacheNames.video, key = "#id")
    public Video updateCache(Long id, int count, int action) {
    	VideoService videoService = SpringContextHolder.getBean(VideoService.class);
        Video video = videoService.getVideoAndRecommend(id);
        switch (action) {
            case BizCst.UserLike.Action.share:
                video.setShareCount(video.getShareCount() + count);
                break;
            case BizCst.UserLike.Action.like:
                video.setPraiseCount(video.getPraiseCount() + count);
                break;
            case BizCst.UserLike.Action.collection:
                video.setFavoriteCount(video.getFavoriteCount() + count);
                break;
            case 10:
                // 播放
                video.setPlayCount(video.getPlayCount() + count);
                break;
            case 11:
                // 评论
                video.setCommentCount(video.getCommentCount() + count);
                break;
            default:
                break;
        }
        return video;
    }

    @Override
    @CachePut(value = CacheNames.video, key = "#id")
    public Video syncCacheToDB(Long id) {
    	VideoService videoService = SpringContextHolder.getBean(VideoService.class);
        Video cache = videoService.getVideoAndRecommend(id);
        if(ObjectUtil.isNotEmpty(cache)) {
            this.update(
                    Wrappers.lambdaUpdate(Video.class)
                            .eq(Video::getId, id)
                            .set(Video::getShareCount, cache.getShareCount())
                            .set(Video::getFavoriteCount, cache.getFavoriteCount())
                            .set(Video::getPraiseCount, cache.getPraiseCount())
                            .set(Video::getCommentCount, cache.getCommentCount())
                            .set(Video::getPlayCount, cache.getPlayCount())
            );
            cache.setUpdateTime(LocalDateTime.now());
        }
        return cache;
    }

    @Override
    @CacheEvict(value = CacheNames.video, key = "#id")
    public void removeCache(Long id) {
    	VideoService videoService = SpringContextHolder.getBean(VideoService.class);
    	videoService.syncCacheToDB(id);
    }

    @Override
    public R play(Long id) {
    	VideoService videoService = SpringContextHolder.getBean(VideoService.class);
        Video cache = videoService.updateCache(id, 1, 10);
        if (Duration.between(cache.getUpdateTime(), LocalDateTime.now()).toSeconds() > TimeCst.TIME_ONE_HOUR)
            videoService.syncCacheToDB(id);
        return R.ok();
    }

    @Override
    public R share(Long id) {
    	VideoService videoService = SpringContextHolder.getBean(VideoService.class);
        Video video = getById(id);
        if (ObjectUtil.isNull(video))
            return R.failed(Hint.NOT_FOUND);
        // 用户行为入库
        UserShare share = new UserShare();
        share.setUid(SecurityUtils.getUser().getId());
        share.setAction(BizCst.UserLike.Action.share);
        share.setType(BizCst.UserLike.Type.video);
        share.setBindId(id);
        userShareService.save(share);
        // 数量统计只修改缓存
        Video cache = videoService.updateCache(id, 1, BizCst.UserLike.Action.share);
        if (Duration.between(cache.getUpdateTime(), LocalDateTime.now()).toSeconds() > TimeCst.TIME_ONE_HOUR)
        	videoService.syncCacheToDB(id);
        return R.ok();
    }

    @Override
    public R like(Long id) {
    	VideoService videoService = SpringContextHolder.getBean(VideoService.class);
        Video video = getById(id);
        if (ObjectUtil.isNull(video))
            return R.failed(Hint.NOT_FOUND);
        // 判断是否已点赞过该视频
        Long uid = SecurityUtils.getUser().getId();
        Set<Long> likeUserSet = videoService.getLikeUserSet(id);
        if (likeUserSet.contains(uid)) {
            // 已点赞，取消点赞，修改数量
            userLikeService.remove(
                    Wrappers.lambdaQuery(UserLike.class)
                            .eq(UserLike::getType, BizCst.UserLike.Type.video)
                            .eq(UserLike::getBindId, id)
                            .eq(UserLike::getUid, uid)
            );
            // 数量统计只修改缓存
            Video cache = videoService.updateCache(id, -1, BizCst.UserLike.Action.like);
            if (Duration.between(cache.getUpdateTime(), LocalDateTime.now()).toSeconds() > TimeCst.TIME_ONE_HOUR)
            	videoService.syncCacheToDB(id);
            // 更新点赞集合缓存
            likeUserSet.remove(uid);
            videoService.updateLikeUserSet(id, likeUserSet);
        } else {
            // 点赞
            userLikeService.save(
                    UserLike.builder()
                            .action(BizCst.UserLike.Action.like)
                            .type(BizCst.UserLike.Type.video)
                            .uid(uid)
                            .bindId(id)
                            .build()
            );
            Video cache = videoService.updateCache(id, 1, BizCst.UserLike.Action.like);
            if (Duration.between(cache.getUpdateTime(), LocalDateTime.now()).toSeconds() > TimeCst.TIME_ONE_HOUR)
            	videoService.syncCacheToDB(id);
            likeUserSet.add(uid);
            videoService.updateLikeUserSet(id, likeUserSet);
        }
        return R.ok();
    }

    @Override
    @Cacheable(value = CacheNames.videoLike, key = "#id")
    public Set<Long> getLikeUserSet(Long id) {
        List<UserLike> list = userLikeService.list(
                Wrappers.lambdaQuery(UserLike.class)
                        .eq(UserLike::getType, BizCst.UserLike.Type.video)
                        .eq(UserLike::getBindId, id)
        );
        if (ObjectUtil.isNotEmpty(list))
            return list.stream()
                    .map(e -> e.getUid())
                    .collect(Collectors.toSet());
        return new HashSet<Long>();
    }

    @Override
    @CachePut(value = CacheNames.videoLike, key = "#id")
    public Set<Long> updateLikeUserSet(Long id, Set<Long> set) {
        return set;
    }

    @Override
    public VideoVO setStatus(Long uid, Video video) {
    	VideoVO vo = BeanUtil.copyProperties(video, VideoVO.class);
        if(ObjectUtil.isNotEmpty(video.getShopId())) {
        	//1.工厂头像
        	Shop shop = shopMapper.selectById(video.getShopId());
        	if(ObjectUtil.isNotNull(shop)) {
        		vo.setShopAvatar(shop.getAvatar());
        		vo.setShopName(shop.getName());
        	}
        	if(ObjectUtil.isNotNull(uid)) {
                //2.是否关注
                long followCount = userFollowService.count(
                        Wrappers.lambdaQuery(UserFollow.class)
                                .eq(UserFollow::getUid, uid)
                                .eq(UserFollow::getBindId, shop.getId())
                                .eq(UserFollow::getType, BizCst.UserFollow.Type.shop)
                );
                if(followCount > 0) {
                	vo.setFollowFlag(true);
                }
        	}
        }
        if(ObjectUtil.isNotNull(uid)) {
        	VideoService videoService = SpringContextHolder.getBean(VideoService.class);
            Set<Long> likeUserSet = videoService.getLikeUserSet(video.getId());
            //3.是否点赞
            if (likeUserSet.contains(uid)) {
            	vo.setLikeFlag(true);
            }
            //4.是否收藏
            Set<Long> collectUserSet = videoService.getCollectUserSet(vo.getId());
            if (collectUserSet.contains(uid)) {
            	vo.setCollectFlag(true);
            }
            //5.是否评论
            Long commentCount = videoCommentMapper.selectCount(
    	        Wrappers.lambdaQuery(VideoComment.class)
    	        .eq(VideoComment::getUid, uid)
    	        .eq(VideoComment::getVideoId, vo.getId())
            );
            if(commentCount > 0) {
            	vo.setCommentFlag(true);
            }
        }
        //6.判断基础收藏数量和基础点赞数量
        vo.setFavoriteCount(Math.max(vo.getFavoriteCount(), vo.getBaseFavoriteCount()));
        vo.setPraiseCount(Math.max(vo.getPraiseCount(), vo.getBasePraiseCount()));
        return vo;
    }

	@Override
	public List<VideoVO> setStatus(Long uid, List<Video> list) {
		ArrayList<VideoVO> res = new ArrayList<VideoVO>();
		for (Video video : list) {
			res.add(this.setStatus(uid, video));
		}
		return res;
	}

    @Override
    public R collect(Long id) {
    	VideoService videoService = SpringContextHolder.getBean(VideoService.class);
        Video video = getById(id);
        if (ObjectUtil.isNull(video))
            return R.failed(Hint.NOT_FOUND);
        // 判断是否已收藏过该视频
        Long uid = SecurityUtils.getUser().getId();
        Set<Long> collectUserSet = videoService.getCollectUserSet(id);
        if (collectUserSet.contains(uid)) {
            // 已收藏，取消收藏，修改数量
            userCollectionService.remove(
                    Wrappers.lambdaQuery(UserCollection.class)
                            .eq(UserCollection::getType, BizCst.UserCollection.Type.video)
                            .eq(UserCollection::getBindId, id)
                            .eq(UserCollection::getUid, uid)
            );
            // 数量统计只修改缓存
            Video cache = videoService.updateCache(id, -1, BizCst.UserLike.Action.collection);
            if (Duration.between(cache.getUpdateTime(), LocalDateTime.now()).toSeconds() > TimeCst.TIME_ONE_HOUR)
            	videoService.syncCacheToDB(id);
            // 更新收藏集合缓存
            collectUserSet.remove(uid);
            videoService.updateCollectUserSet(id, collectUserSet);
        } else {
            // 收藏
            userCollectionService.save(
                    UserCollection.builder()
                            .type(BizCst.UserCollection.Type.video)
                            .uid(uid)
                            .bindId(id)
                            .build()
            );
            Video cache = videoService.updateCache(id, 1, BizCst.UserLike.Action.collection);
            if (Duration.between(cache.getUpdateTime(), LocalDateTime.now()).toSeconds() > TimeCst.TIME_ONE_HOUR)
            	videoService.syncCacheToDB(id);
            collectUserSet.add(uid);
            videoService.updateCollectUserSet(id, collectUserSet);
        }
        return R.ok();
    }

    @Override
    @Cacheable(value = CacheNames.videoCollection, key = "#id")
    public Set<Long> getCollectUserSet(Long id) {
        List<UserCollection> list = userCollectionService.list(
                Wrappers.lambdaQuery(UserCollection.class)
                        .eq(UserCollection::getType, BizCst.UserCollection.Type.video)
                        .eq(UserCollection::getBindId, id)
        );
        if (ObjectUtil.isNotEmpty(list))
            return list.stream()
                    .map(e -> e.getUid())
                    .collect(Collectors.toSet());
        return new HashSet<Long>();
    }

    @Override
    @CachePut(value = CacheNames.videoCollection, key = "#id")
    public Set<Long> updateCollectUserSet(Long id, Set<Long> set) {
        return set;
    }

    @Override
    public Page<Video> getFollowPage(Page page) {
        Long uid = SecurityUtils.getUser().getId();
        if (ObjectUtil.isNotNull(uid)) {
            return baseMapper.getFollowPage(page, uid);
        }
        return page;
    }

}
